/**
 * The base plugin container interface that all plugin containers are REQUIRED to implement.  Some of the functions are pre-implemented, but may be overridden by the implementing container if needed.
 */
jsx3.lang.Class.defineInterface(
        "ux.core.plugin.PluginContainerInterface",
        null,
        function(container, container_prototype) {

            /**
             * {jsx3.util.List} a list of the plugin component instances loaded for this container
             * @private
             * @jsxobf-clobber
             */
            container_prototype.pluginList = null;

            /**
             * Abstract method to be implemented by implementing classes.  This will be called on each child plugin component instance
             * immediately after it's constructor is called, but before it's <code>initPlugin()</code> function is called.  This
             * allows for custom initialization to be done on the plugin objects before they are used.
             *
             * @param obj {Object} the object instance of the child plugin
             */
            container_prototype.setupPlugin = jsx3.lang.Method.newAbstract("obj");

            /**
             * Abstract method to be implemented by implementing classes.  This is called after all of the child plugin components
             * have been loaded, instantiated, and initialized.  This is where you make your class do something with the loaded
             * child plugins once they are ready to use.
             */
            container_prototype.onAfterGetPlugins = jsx3.lang.Method.newAbstract();

            container_prototype.isValidPlugin = function(obj) {
                try {
                    return obj.instanceOf("ux.core.plugin.PluginInterface");
                }
                catch(ex) {
                    return false;
                }
            }

            container_prototype.destroyPlugin = function() {
                var iter = this.getPluginList().iterator();
                while(iter.hasNext()) {
                    var nextPlugin = iter.next();
                    try {
                        nextPlugin.destroyPlugin();
                    }
                    catch(ex) {
                        ux.core.System.logException(ex);
                    }
                }
            }

            /**
             * Either requests the implementing plugin components for this plugin container, or notifies the container
             * that they are ready to be used if we have already loaded them.  Should be called by the container when
             * they are ready to retrieve their child plugins.
             *
             * @param interfaces {String | jsx3.util.List} The fully-qualified name of the interface to find implementing plugin
             *                     components for, or a list of them.
             * @param preLoadList {jsx3.util.List} a list of <code>jsx3.lang.Class</code> objects to treat as plugin classes
             *                    to be loaded prior to loading plugin classes.  This allows the internal loading of your own
             *                    components into the plugin list, prior to the plugins.  This also allows you to mandate that
             *                    your internal components appear first.  The classes in this list should NOT be specified as
             *                    implementing classes of <code>interfaces</code> in ANY of the <code>component.xml</code>
             *                    descriptor files!
             */
            container_prototype.loadPlugins = function(interfaces, preLoadList, id) {
                if(this.getPluginList().size() == 0) {
                    if(preLoadList) {
                        var iter = preLoadList.iterator();
                        while(iter.hasNext()) {
                            try {
                                var obj = iter.next().getInstance(this.getPluginInitArgs());
                                if(this.isValidPlugin(obj)) {
                                    if(obj) {
                                        try {
                                            obj.initPlugin();
                                        }
                                        catch(ex1) {
                                            ux.core.System.logException(ex1);
                                        }
                                        try {
                                            var component = obj.getComponentPath()
                                            if(component) {
                                                var config = ux.core.System.createConfig(ux.core.System.resolveURI(obj.getNamespace(), component));
                                                obj.setPluginConfig(config)
                                                ux.core.util.Log.SYSTEM.info("Set config for object " + obj + ". config " + config)
                                            }
                                            else {
                                                ux.core.util.Log.SYSTEM.info("Can't load component getComponentPath returns . " + component);
                                            }
                                        }
                                        catch(e) {
                                            ux.core.util.Log.SYSTEM.info("Can't load component. " + e);
                                        }
                                        this.setupPlugin(obj);
                                        var me = this;
                                        obj.callbackTarget = me;
                                        obj.beginEditCallback = me.containerBeginEdit;
                                        obj.endEditCallback = me.containerEndEdit;
                                        var loadstatus = 200  ;
                                        var isalowed = false ;
                                        try {
                                            var uiResources = obj.getPluginConfig().getResources().getUIResources();
//                                            if(uiResources.hasNext()) {
                                            while(uiResources.hasNext()) {
                                                var uiResource = uiResources.next()
                                                var parentExtensionPoints = uiResource.getParentExtensionPoints()
                                                var pExtensionPoint;
                                                loadstatus = ux.core.System.preLoadUIResource(obj.getNamespace(), uiResource.getPath())
                                                if(loadstatus == 403) {
                                                    ux.core.util.Log.SYSTEM.info("User dose not have access right to parent extension points " + parentExtensionPoints + " in plugin component  " + obj)
                                                }
                                                else {
                                                    isalowed = true;
                                                    while(parentExtensionPoints.hasNext()) {
                                                        parentExtensionPoint = parentExtensionPoints.next()
                                                        this.getPluginList(parentExtensionPoint).add(obj, this.getPluginList(parentExtensionPoint).size())
                                                    }
                                                }
                                            }
//                                            } else {
//                                                isalowed = true;
//                                            }
                                        }
                                        catch(e) {
                                            isalowed = true
                                            ux.core.util.Log.SYSTEM.info(" Faild to preloaed UI resource. Plugin:  " + obj)
                                        }
                                        if(isalowed) {
                                            this.getPluginList().add(obj, this.getPluginList().size());
                                            ux.core.util.Log.SYSTEM.info(" Added plugin  " + obj + " to the plugin list.")
                                        }
                                        else {
                                            ux.core.util.Log.SYSTEM.info("User dose not have access right to Plugin   " + obj)
                                        }
                                    }
                                }
                                else {
                                    jsx3.error(obj.jsxclass + " is not a valid plugin. It must implement ux.core.plugin.PluginInterface. Known interfaces it implements are:");
                                    for(name in obj) {
                                        jsx3.error("     Field: " + name);
                                    }
                                    try {
                                        var arr = obj.jsxclass.getInterfaces();
                                        for(var i = 0; i < arr.length; i++) {
                                            jsx3.error("     " + arr[i].getName());
                                        }
                                    }
                                    catch(ex1) {
                                        ux.core.System.logException(ex1);
                                    }
                                }
                            }
                            catch(ex) {
                                ux.core.System.logException(ex);
                            }
                        }
                    }
                    try {
                        var caller = jsx3.lang.getCaller();
                        var callId = caller.jsxmethod + "_" + (id ? id : "defaultId");
                        ux.core.System.getImplementingPlugins(interfaces, this, this.setImplementingPlugins, callId, false);
                    }
                    catch(ex) {
                        ux.core.System.logException(ex);
                    }
                }
                else {
                    this.onAfterGetPlugins();
                }
            }

            container_prototype.getPreLoadList = function() {
                if(this.preLoadList) {
                    this.preLoadList = new jsx3.util.List(0);
                }
                return this.preLoadList;
            }

            container_prototype.addPreLoadComponent = function(nameSpace, componentPath, classname, extensionPoints) {

            }

            /**
             * The function that will be used by the ux.core.System class to do a callback to hand over the
             * ux.core.util.component.ComponentConfig objects for the implementing plugin components returned
             * from the server in the <code>ux.core.System.getImplementingPlugins()</code> call.  Loads the
             * implementing classes, gets an instance of them, initializes the instances, adds them to the <code>pluginList</code>,
             * and then requests that they be loaded.
             *
             * @param plugins {Object | jsx3.lang.Exception} an associative array of the <code>ux.core.util.component.ComponentConfig</code>
             *                objects for the implementing plugin components, indexed by the fully-qualified name of their
             *                implementation class; or an exception that was thrown while trying to get the implementing
             *                plugin components (this wold indicate the exception was fatal, and thus no plugins were successfully
             *                retrieved)
             * @param exception {jsx3.lang.Exception} an exception thrown while getting the implementing plugins.  Typically this
             *                   is not a fatal exception, and thus the <code>plugins</code> argument may still have valid
             *                   plugins that were loaded.
             * @private
             */
            container_prototype.setImplementingPlugins = function(plugins, exception) {
                if(exception) { // these exceptions are non-fatal to the loading...it means that only SOME of the plugins could not be loaded
                    ux.core.System.logException(exception);
                }
                if(plugins) {
                    for(var clazz in plugins) {
                        var config = plugins[clazz];
                        var load = true;
                        try {
                            if(jsx3.eval("(" + clazz + ")")) {
                                load = false;
                            }
                        }
                        catch(ex) {
                            // do nothing...this class SHOULD get loaded then.
                        }
                        try {
                            if(load) {
                                ux.core.System.loadClassByName(clazz, config);
                            }
                            var obj = ux.core.System.getPluginClassInstance(clazz, this.getPluginInitArgs());
                            if(obj) {
                                obj.setPluginConfig(config);
                                try {
                                    obj.initPlugin();
                                }
                                catch(ex1) {
                                    ux.core.System.logException(ex1);
                                }
                                this.setupPlugin(obj);
                                var me = this;
                                obj.callbackTarget = me;
                                obj.beginEditCallback = me.containerBeginEdit;
                                obj.endEditCallback = me.containerEndEdit;
                                var loadstatus = 200  ;
                                var isalowed = false ;
                                try {
                                    var uiResources = obj.getPluginConfig().getResources().getUIResources()
                                    while(uiResources.hasNext()) {
                                        var uiResource = uiResources.next()
                                        var parentExtensionPoints = uiResource.getParentExtensionPoints()
                                        var pExtensionPoint;
                                        loadstatus = ux.core.System.preLoadUIResource(obj.getNamespace(), uiResource.getPath())
                                        if(loadstatus == 403) {
                                            ux.core.util.Log.SYSTEM.info("User dose not have access right to parent extension points " + parentExtensionPoints + " in plugin component  " + obj)
                                        }
                                        else {
                                            isalowed = true;
                                            while(parentExtensionPoints.hasNext()) {
                                                parentExtensionPoint = parentExtensionPoints.next()
                                                this.getPluginList(parentExtensionPoint).add(obj, this.getPluginList(parentExtensionPoint).size())
                                            }
                                        }
                                    }
                                }
                                catch(e) {
                                    isalowed = true
                                    ux.core.util.Log.SYSTEM.info(" Faild to preloaed UI resource. Plugin:  " + obj)
                                }
                                if(isalowed) {
                                    this.getPluginList().add(obj, this.getPluginList().size());
                                    ux.core.util.Log.SYSTEM.info(" Added plugin  " + obj + " to the plugin list.")
                                }
                                else {
                                    ux.core.util.Log.SYSTEM.info("User dose not have access right to Plugin   " + obj)
                                }
                            }
                        }
                        catch(ex) {
                            ux.core.System.logException(ex);
                        }
                    }
                    this.onAfterGetPlugins();
                }
                else {
                    jsx3.log("No plugins found to add to plugin container " + this.getClass().getName());
                }
            }

            container_prototype.onAfterContainerUnloaded = function() {
                var iter = this.getPluginList().iterator();
                while(iter.hasNext()) {
                    var nextPlugin = iter.next();
                    try {
                        nextPlugin.unload();
                    }
                    catch(ex) {
                        ux.core.System.logException(ex);
                    }
                }
                this.onAfterComponentUnloaded();
            }

            container_prototype.unload = function() {
                this.onAfterContainerUnloaded();
            }

            container_prototype.onAfterComponentUnloaded = function() {
                try {
                    if(this.isInEditMode() && this.endEditMode) {
                        this.endEditMode();
                    }
                    ux.core.System.logException(new jsx3.Exception("WARNING!!!!   The plugin " + this.getClass().getName() + " does not override the onAfterComponentUnloaded function to perform cleanup after it's main UI component is unloaded."));
                }
                catch (ex) {
                    // occasionally, harmless errors occur here when the ui is gone
                }
            }

            container_prototype.isInEditMode = function() {
                if(this.isEditMode) {
                    return true;
                }
                return this.childIsInEditMode();
            }

            container_prototype.childIsInEditMode = function() {
                var isEdit = false;
                var iter = this.getPluginList().iterator();
                while(iter.hasNext()) {
                    try {
                        var plugin = iter.next();
                        isEdit = plugin.isInEditMode();
                        if(isEdit) {
                            break;
                        }
                    }
                    catch(ex) {
                        // do nothing...treat it as false
                    }
                }
                return isEdit;
            }

            /**
             * Gets the list of child plugin component instances for this plugin container.  This method should NOT be
             * overridden by implementing plugins.
             * If extensionPoint not null then returns only the list of child plugin component instances for this plugin container that
             * user has access right for it's UI resource.
             *
             * @param  extensionPoint {String}
             * @return {jsx3.util.List} the list of child plugin component instances
             */
            container_prototype.getPluginList = function(extensionPoint) {
                if(this.pluginList == null) {
                    this.pluginList = new  jsx3.util.List(0);
                }
                if(extensionPoint) {
                    if(this.filteredPluginListObj == null) {
                        this.filteredPluginListObj = new  Object();
                    }
                    if(this.filteredPluginListObj[extensionPoint] == null)
                        this.filteredPluginListObj[extensionPoint] = new  jsx3.util.List(0);
                    return this.filteredPluginListObj[extensionPoint]
                }
                else {
                    return this.pluginList;
                }
            }

            container_prototype.getPluginInitArgs = function() {
                return null;
            }

            container_prototype.containerBeginEdit = function(objPlugin) {
                this.beginEdit(objPlugin);
            }

            container_prototype.containerEndEdit = function(objPlugin) {
                this.endEdit(objPlugin);
            }

            container_prototype.beginEdit = function(objPlugin) {
            }

            container_prototype.endEdit = function(objPlugin) {
            }

            container_prototype._allowComponentUnload = function(target, fctnYes, fctnNo, args) {
                if(!this.isEditMode) {
                    fctnYes.call(target, args);
                }
                else {
                    var me = this;
                    ux.core.System.confirm(this.getConfirmTitle(), this.getConfirmText(),
                            function(dialog) {
                                dialog.doClose();
                                me.doCancelEdits();
                                fctnYes.call(target, args);
                            },
                            function(dialog) {
                                dialog.doClose();
                                if(fctnNo) {
                                    fctnNo.call(target, args);
                                }
                            },
                            this.getConfirmYesButtonText(), this.getConfirmNoButtonText(), this.getDefaultConfirmButton());
                }
            }

            container_prototype.getConfirmTitle = function() {
                return ux.core.System.getServer().getDynamicProperty("@uxcore10@PluginContainer Default Confirm Discard Title");
            }

            container_prototype.getConfirmText = function() {
                return ux.core.System.getServer().getDynamicProperty("@uxcore10@PluginContainer Default Confirm Discard Text");
            }

            container_prototype.getConfirmYesButtonText = function() {
                return ux.core.System.getServer().getDynamicProperty("@uxcore10@Btn Yes");
            }

            container_prototype.getConfirmNoButtonText = function() {
                return ux.core.System.getServer().getDynamicProperty("@uxcore10@Btn No");
            }

            container_prototype.getDefaultConfirmButton = function() {
                return 2;
            }

            container_prototype.childAllowsUnload = function(obj) {
                if(obj.iter.hasNext()) {
                    var me = this;
                    try {
                        obj.iter.next().allowComponentUnload(me, me.childAllowsUnload, me.childDisallowsUnload, obj);
                    }
                    catch(ex) {
                        // if the component throws an exception, treat it as a "true" so as not to block the switch
                        // due to a badly written component
                        me.childAllowsUnload(obj);
                    }
                }
                else {
                    this._allowComponentUnload(obj.target, obj.fctnYes, obj.fctnNo, obj.args);
                }
            }

            container_prototype.childDisallowsUnload = function(obj) {
                if(obj.fctnNo) {
                    obj.fctnNo.call(obj.target, obj.args);
                }
            };

            container_prototype.allowComponentUnload = function(target, fctnYes, fctnNo, args) {
                var obj = new Object();
                obj.iter = this.getPluginList().iterator();
                obj.target = target;
                obj.fctnYes = fctnYes;
                obj.fctnNo = fctnNo;
                obj.args = args;
                var me = this;
                if(obj.iter.hasNext()) {
                    obj.iter.next().allowComponentUnload(me, me.childAllowsUnload, me.childDisallowsUnload, obj);
                }
                else {
                    this._allowComponentUnload(target, fctnYes, fctnNo, args);
                }
            };

            container_prototype.doCancelEdits = function() {
            }
        }
        );